package com.btech.dftp.dataimport.excel;

import java.io.BufferedInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.function.Consumer;

import org.apache.commons.io.FilenameUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.quincy.rock.core.function.EachConsumer;
import org.quincy.rock.core.util.IOUtil;
import org.quincy.rock.core.util.StringUtil;

import com.btech.dftp.dataimport.DataUtils;
import com.btech.dftp.dataimport.Field;

/**
 * <b>主细表ExcelReader。</b>
 * <p><b>详细说明：</b></p>
 * <!-- 在此添加详细说明 -->
 * 无。
 * <p><b>修改列表：</b></p>
 * <table width="100%" cellSpacing=1 cellPadding=3 border=1>
 * <tr bgcolor="#CCCCFF"><td>序号</td><td>作者</td><td>修改日期</td><td>修改内容</td></tr>
 * <!-- 在此添加修改列表，参考第一行内容 -->
 * <tr><td>1</td><td>wks</td><td>2022年4月22日 下午3:04:49</td><td>建立类型</td></tr>
 * 
 * </table>
 * @version 1.0
 * @author wks
 * @since 1.0
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class DetailExcelReader implements Closeable {
	private Workbook book;
	private int sheetIndex;
	private int firstRow;
	private int lastRow;
	private Field mainKeyField;
	private Field detailKeyField;
	private int batchSize;
	
	private final List<Field> mainFields = new ArrayList<Field>();
	private final List<Field> detailFields = new ArrayList<Field>();

	/**
	 * <b>构造方法。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param fileName
	 * @throws IOException
	 */
	public DetailExcelReader(String fileName) throws IOException {
		boolean isOld = "xls".equalsIgnoreCase(FilenameUtils.getExtension(fileName));
		File excelFile = new File(fileName);
		File cfgFile = new File(FilenameUtils.removeExtension(fileName) + ".cfg");
		if (!excelFile.exists())
			throw new IOException("Excel file not found:" + excelFile.getAbsolutePath());
		if (!cfgFile.exists())
			throw new IOException("Config file not found:" + cfgFile.getAbsolutePath());
		loadConfig(cfgFile);
		InputStream is = new BufferedInputStream(new FileInputStream(excelFile));
		book = isOld ? new HSSFWorkbook(is) : new XSSFWorkbook(is);
	}

	/**
	 * <b>getBatchSize。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @return
	 */
	public int getBatchSize() {
		return batchSize;
	}

	private void loadConfig(File cfgFile) throws IOException {
		Properties props = IOUtil.loadProperties(cfgFile, StringUtil.UTF_8.name());
		batchSize=Integer.parseInt(props.getProperty("app.batchSize", "20"));
		sheetIndex = Integer.parseInt(props.getProperty("sheet.sheetIndex", "0"));
		firstRow = Integer.parseInt(props.getProperty("sheet.firstRow", "2"));
		lastRow = Integer.parseInt(props.getProperty("sheet.lastRow", "0"));
		int mainKeyIndex = Integer.parseInt(props.getProperty("sheet.mainKeyIndex", "0"));
		int detailKeyIndex = Integer.parseInt(props.getProperty("sheet.detailKeyIndex", "0"));

		for (String key : props.stringPropertyNames()) {
			if (key.startsWith("main.")) {
				final Field field = new Field();
				field.name = key.substring(5);
				String value = props.getProperty(key);
				StringUtil.split(value, StringUtil.CHAR_COMMA, new EachConsumer<CharSequence>() {

					public void each(int index, CharSequence t) {
						if (index == 0)
							field.colIndex = Integer.parseInt(t.toString().trim());
						else if (index == 1)
							field.dataType = DataUtils.toDataType(t.toString());
					}
				});
				//
				if (field.colIndex == mainKeyIndex)
					mainKeyField = field;
				//	
				mainFields.add(field);
			} else if (key.startsWith("detail.")) {
				final Field field = new Field();
				field.name = key.substring(7);
				String value = props.getProperty(key);
				StringUtil.split(value, StringUtil.CHAR_COMMA, new EachConsumer<CharSequence>() {

					public void each(int index, CharSequence t) {
						if (index == 0)
							field.colIndex = Integer.parseInt(t.toString().trim());
						else if (index == 1)
							field.dataType = DataUtils.toDataType(t.toString());
					}
				});
				if (field.colIndex == detailKeyIndex)
					detailKeyField = field;
				detailFields.add(field);
			}
		}
	}

	private Map<String, Object> getRowData(Row row, boolean isMain) {
		Field keyField = isMain ? mainKeyField : detailKeyField;
		Cell cell = row.getCell(keyField.colIndex);
		Object value = DataUtils.getCellValue(cell, keyField.dataType);
		if (value == null)
			return null;
		List<Field> fields = isMain ? mainFields : detailFields;
		Map<String, Object> data = new HashMap<String, Object>();
		for (Field f : fields) {
			cell = row.getCell(f.colIndex);
			value = DataUtils.getCellValue(cell, f.dataType);
			data.put(f.name, value);
		}		
		return data;
	}

	private boolean isBlankRow(Row row) {
		if (row == null)
			return true;
		//检查主表字段
		for (Field f : mainFields) {
			Cell cell = row.getCell(f.colIndex);
			Object v = DataUtils.getCellValue(cell, f.dataType);
			if (v != null)
				return false;
		}
		//检查明细字段
		for (Field f : detailFields) {
			Cell cell = row.getCell(f.colIndex);
			Object v = DataUtils.getCellValue(cell, f.dataType);
			if (v != null)
				return false;
		}

		return true;
	}

	/**
	 * <b>forEach。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param data
	 */

	public void forEach(Consumer<Map<String, Object>> callback) {
		Sheet sheet = book.getSheetAt(sheetIndex);
		if (lastRow <= 0)
			lastRow = sheet.getLastRowNum();
		Map<String, Object> mainData = null;
		for (int i = firstRow; i <= lastRow; i++) {
			Row row = sheet.getRow(i);
			if (isBlankRow(row))
				continue; //忽略空白行
			//取主数据			
			if (mainData == null) {
				mainData = getRowData(row, true);
				if (mainData == null)
					continue;
			} else {
				Object oldKey = mainData.get(mainKeyField.name);
				Cell cell = row.getCell(mainKeyField.colIndex);
				Object newKey = DataUtils.getCellValue(cell, mainKeyField.dataType);
				if (newKey != null && !newKey.equals(oldKey)) {
					//处理老数据
					callback.accept(mainData);
					//开启新数据
					mainData = getRowData(row, true);
				}
			}
			//取明细数据
			List<Map<String, Object>> detailList = (List) mainData.get("detail");
			if (detailList == null) {
				detailList = new ArrayList<Map<String, Object>>();
				mainData.put("detail", detailList);
			}
			Map<String, Object> detailData = getRowData(row, false);
			if (detailData != null)
				detailList.add(detailData);
		}
		if (mainData != null) {
			callback.accept(mainData);
		}
	}

	/** 
	 * close。
	 * @see java.io.Closeable#close()
	 */
	public void close() throws IOException {
		book.close();
	}
}
